package com.meilingcloud.research.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.meilingcloud.biobank.domain.Source;
import com.meilingcloud.biobank.domain.SourceType;
import com.meilingcloud.biobank.service.ISourceService;
import com.meilingcloud.biobank.service.ISourceTypeService;
import com.meilingcloud.common.enums.DateType;
import com.meilingcloud.common.enums.FollowState;
import com.meilingcloud.common.utils.DateUtils;
import com.meilingcloud.common.utils.SecurityUtils;
import com.meilingcloud.research.domain.RwsFollow;
import com.meilingcloud.research.domain.RwsFollowTemp;
import com.meilingcloud.research.mapper.RwsFollowMapper;
import com.meilingcloud.research.service.IRwsFollowService;
import com.meilingcloud.research.service.IRwsFollowTempService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 随访列表Service业务层处理
 *
 * @author chenl
 * @date 2021-04-21
 */
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Service
public class RwsFollowServiceImpl extends ServiceImpl<RwsFollowMapper, RwsFollow> implements IRwsFollowService {

    private final static String TO_FOLLOW = "1";
    private final ISourceService sourceService;
    private final ISourceTypeService sourceTypeService;
    private final IRwsFollowTempService followTempService;

    @Override
    public List<RwsFollow> queryList(RwsFollow rwsFollow) {
        LambdaQueryWrapper<RwsFollow> lqw = Wrappers.lambdaQuery();
        if (rwsFollow.getSourceId() != null) {
            lqw.eq(RwsFollow::getSourceId, rwsFollow.getSourceId());
        }
        if (CollectionUtil.isNotEmpty(rwsFollow.getFollowPlanRange())) {
            lqw.between(RwsFollow::getFollowPlan, rwsFollow.getFollowPlanRange().get(0), rwsFollow.getFollowPlanRange().get(1));
        }
        if (StringUtils.isNotBlank(rwsFollow.getFollowState())) {
            lqw.eq(RwsFollow::getFollowState, rwsFollow.getFollowState());
        }
        if (StringUtils.isNotBlank(rwsFollow.getFollowWay())) {
            lqw.eq(RwsFollow::getFollowWay, rwsFollow.getFollowWay());
        }
        return this.list(lqw);
    }

    @Override
    public List<RwsFollow> handleFollowList(List<RwsFollow> list) {
        if (CollectionUtil.isEmpty(list)) {
            return list;
        }
        List<Long> sourceIds = list.stream().map(RwsFollow::getSourceId).collect(Collectors.toList());
        List<Source> sources = sourceService.listByIds(sourceIds);
        List<RwsFollow> collect = list.stream().map(item -> sources.stream().filter(s -> ObjectUtil.equal(item.getSourceId(), s.getSourceId()))
                .findAny()
                .map(s -> {
                    SourceType sourceType = sourceTypeService.queryById(s.getSourceTypeId());
                    item.setSourceCode(s.getSourceCode());
                    item.setSourceName(s.getSourceName());
                    //item.setSourceTypeName(sourceType.getSourceTypeName());
                    return item;
                })
                .orElse(null)).collect(Collectors.toList());
        return collect;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveFollow(RwsFollow rwsFollow) {
        boolean result;
        // 模板随访
        if (ObjectUtil.isNotEmpty(rwsFollow.getFollowTempId())) {
            RwsFollowTemp rwsFollowTemp = followTempService.getById(rwsFollow.getFollowTempId());
            if (ObjectUtil.isNotEmpty(rwsFollowTemp)) {
                rwsFollow.setIntervalTime(rwsFollowTemp.getIntervalTime());
                rwsFollow.setIntervalUnit(rwsFollowTemp.getIntervalUnit());
                rwsFollow.setFollowCount(rwsFollowTemp.getFollowCount());
            }
        }
        // 规律性随访
        if (ObjectUtil.isNotEmpty(rwsFollow.getIntervalTime()) && rwsFollow.getIntervalTime() > 0
                && StringUtils.isNotBlank(rwsFollow.getIntervalUnit()) && rwsFollow.getFollowCount() > 0) {
            List<RwsFollow> rwsFollowList = new ArrayList<>();
            DateType dateType = DateType.getTypeByCode(rwsFollow.getIntervalUnit());
            for (int i = 0; i < rwsFollow.getFollowCount(); i++) {
                RwsFollow follow = new RwsFollow();
                follow.setSourceId(rwsFollow.getSourceId());
                follow.setFollowNum(i + 1);
                follow.setFollowVisitor(rwsFollow.getFollowVisitor());
                follow.setFollowState(rwsFollow.getFollowState());
                follow.setFollowDesc(rwsFollow.getFollowDesc());
                follow.setFollowWay(rwsFollow.getFollowWay());
                follow.setFormKey(rwsFollow.getFormKey());
                switch (dateType) {
                    case HOUR:
                        follow.setFollowPlan(DateUtils.addDateByHour(rwsFollow.getFollowPlan(), i * rwsFollow.getIntervalTime()));
                        break;
                    case DAY:
                        follow.setFollowPlan(DateUtils.addDateByDay(rwsFollow.getFollowPlan(), i * rwsFollow.getIntervalTime()));
                        break;
                    case WEEK:
                        follow.setFollowPlan(DateUtils.addDateByWeek(rwsFollow.getFollowPlan(), i * rwsFollow.getIntervalTime()));
                        break;
                    case MONTH:
                        follow.setFollowPlan(DateUtils.addDateByMonth(rwsFollow.getFollowPlan(), i * rwsFollow.getIntervalTime()));
                        break;
                    case YEAR:
                        follow.setFollowPlan(DateUtils.addDateByYear(rwsFollow.getFollowPlan(), i * rwsFollow.getIntervalTime()));
                        break;
                    default:
                        break;
                }
                rwsFollowList.add(follow);
            }
            result = this.saveBatch(rwsFollowList);
        } else {
            // 单次随访
            result = this.save(rwsFollow);
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateFollowById(RwsFollow rwsFollow) {
        boolean result = this.updateById(rwsFollow);
        List<RwsFollow> list = this.list(new LambdaQueryWrapper<RwsFollow>().eq(RwsFollow::getSourceId, rwsFollow.getSourceId()));
        if (CollectionUtil.isNotEmpty(list)) {
            Source source = new Source();
            source.setSourceId(rwsFollow.getSourceId());
            if (list.size() == 1) {
                source.setFollowState(rwsFollow.getFollowState());
            } else if (list.size() > 1) {
                Set<String> collect = list.stream().map(RwsFollow::getFollowState).collect(Collectors.toSet());
                if (collect.contains(FollowState.TO_FOLLOW.getCode())) {
                    source.setFollowState(FollowState.TO_FOLLOW.getCode());
                } else if (collect.contains(FollowState.RELAPSED.getCode())) {
                    source.setFollowState(FollowState.RELAPSED.getCode());
                } else if (Collections.frequency(collect, FollowState.NO_NEED.getCode()) == collect.size()) {
                    source.setFollowState(FollowState.NO_NEED.getCode());
                } else if (Collections.frequency(collect, FollowState.FOLLOWED.getCode()) == collect.size()) {
                    source.setFollowState(FollowState.FOLLOWED.getCode());
                } else if (Collections.frequency(collect, FollowState.IGNORED.getCode()) == collect.size()) {
                    source.setFollowState(FollowState.IGNORED.getCode());
                } else if (Collections.frequency(collect, FollowState.TERMINATED.getCode()) == collect.size()) {
                    source.setFollowState(FollowState.TERMINATED.getCode());
                } else if (Collections.frequency(collect, FollowState.LOST.getCode()) == collect.size()) {
                    source.setFollowState(FollowState.LOST.getCode());
                }
            }
            // 同步更新样本源状态
            sourceService.updateById(source);
        }
        return result;
    }

    @Override
    public boolean updateFollowBatch(List<RwsFollow> rwsFollows) {
        String userName = SecurityUtils.getUsername();
        List<RwsFollow> collect = rwsFollows.stream().map(item -> {
            item.setUpdateBy(userName);
            return item;
        }).collect(Collectors.toList());
        return this.updateBatchById(collect);
    }

    @Override
    public RwsFollow getFollow(Long followId) {
        RwsFollow rwsFollow = this.getById(followId);
        if (ObjectUtil.isNotEmpty(rwsFollow)) {
            Source source = sourceService.getById(rwsFollow.getSourceId());
            rwsFollow.setSourceCode(source.getSourceCode());
            rwsFollow.setSourceName(source.getSourceName());
        }
        return rwsFollow;
    }

    @Override
    public Integer getToFollowCount() {
        return this.count(new LambdaQueryWrapper<RwsFollow>().eq(RwsFollow::getFollowState, TO_FOLLOW));
    }

    @Override
    public Map<String, Integer> statisticFollow() {
        int currentMonth = DateUtils.getCurrentMonth();
        int currentYear = DateUtils.getCurrentYear();
        Date firstDayOfMonth = DateUtils.getFirstDayOfMonth(currentYear, currentMonth);
        Date lastDayOfMonth = DateUtils.getLastDayOfMonth(currentYear, currentMonth);
        List<RwsFollow> rwsFollows = this.list(new LambdaQueryWrapper<RwsFollow>().between(RwsFollow::getFollowPlan, firstDayOfMonth, lastDayOfMonth)
                .eq(RwsFollow::getFollowState, TO_FOLLOW));
        Map<String, Integer> map = new HashMap<>();
        rwsFollows.forEach(item -> {
            String followPlan = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, item.getFollowPlan());
            if (!map.containsKey(followPlan)) {
                map.put(followPlan, 1);
            } else {
                int i = map.get(followPlan) + 1;
                map.put(followPlan, i);
            }
        });
        return map;
    }
}
